Introduction

In this project we are trying first to explore our data to get a better understanding To answer our questions we first need to have a preliminary look at our data, so that we can get a better a idea what we are dealing with, as well as the possible missing data and relationships that exist

Preliminary Look at the data

We need first to define the data we have.

Variable Definition Key
survival Survival 0 = No, 1 = yes
pclass ticket class 1 = 1st, 2 = 2nd, 3 = 3rd
sex sex
age Age in year
sibsp Number of siblings/spouses aboard the titanic
parch Number of parents/children aboard the Titanic
ticket ticket number(unique)
fare Passenger fare
cabin Cabin number
embarked port of embarkation C = Cherbourg, Q = Queens-town, S = Southampton

Loading the packages and the Data

# Loading Packages
## tidyverse loads dplyr and readr
library(tidyverse)

## To have different color maps
library(viridis)

## ggplot2 to produce different plots
library(ggplot2)

## uses ggplot2 to produce a correlation matrix -- the data must be in the correct form
library(ggcorrplot)

## Gives us better themes
library(hrbrthemes)

## to use skewness fun. to calculate skewness of the distribution
library(e1071)

## Multivariate imputation using chained equations -- to impute the missing values in our data
library(mice)

## Loads different statistical functions
library(statsr)

## To produce interactive plot
library(plotly)


# Loading Training Data
train <- read_csv("data/train.csv")

# Loading Testing Data
test <- read_csv("data/test.csv")

# Binding them into a full data frame
df <- bind_rows(train,test)

Exploration Of The Data

Description of the Data

Summary of Data

summary(train)
  PassengerId       Survived          Pclass          Name               Sex                 Age       
 Min.   :  1.0   Min.   :0.0000   Min.   :1.000   Length:891         Length:891         Min.   : 0.42  
 1st Qu.:223.5   1st Qu.:0.0000   1st Qu.:2.000   Class :character   Class :character   1st Qu.:20.12  
 Median :446.0   Median :0.0000   Median :3.000   Mode  :character   Mode  :character   Median :28.00  
 Mean   :446.0   Mean   :0.3838   Mean   :2.309                                         Mean   :29.70  
 3rd Qu.:668.5   3rd Qu.:1.0000   3rd Qu.:3.000                                         3rd Qu.:38.00  
 Max.   :891.0   Max.   :1.0000   Max.   :3.000                                         Max.   :80.00  
                                                                                        NA's   :177    
     SibSp           Parch           Ticket               Fare           Cabin          
 Min.   :0.000   Min.   :0.0000   Length:891         Min.   :  0.00   Length:891        
 1st Qu.:0.000   1st Qu.:0.0000   Class :character   1st Qu.:  7.91   Class :character  
 Median :0.000   Median :0.0000   Mode  :character   Median : 14.45   Mode  :character  
 Mean   :0.523   Mean   :0.3816                      Mean   : 32.20                     
 3rd Qu.:1.000   3rd Qu.:0.0000                      3rd Qu.: 31.00                     
 Max.   :8.000   Max.   :6.0000                      Max.   :512.33                     
                                                                                        
   Embarked        
 Length:891        
 Class :character  
 Mode  :character  
                   
                   
                   
                   

Categories of Features

Quantitative data are measures of values or counts and are expressed as numbers.

Qualitative data are measures of ‘types’ and may be represented by a name, symbol, or a number code.

Qualitive

Categorical: Survived, Sex, and Embarked. Ordinal: Pclass. Nominal: Name.

Quantitive

Continuous: Age, Fare. Discrete: SibSp, Parch.

Mix types

Ticket is a mix of numeric and alphanumeric data types Cabin is mix between alpha and numeric

Exploring Missing Data.

Checking for Missing values in each feature

colSums(is.na(train))
PassengerId    Survived      Pclass        Name         Sex         Age       SibSp       Parch 
          0           0           0           0           0         177           0           0 
     Ticket        Fare       Cabin    Embarked 
          0           0         687           2 
missing_values <- train %>% summarize_all(funs(sum(is.na(.))/n()))

missing_values <- gather(missing_values, key="feature", value="missing_pct")

missing_values

missing_values %>% 
  ggplot(aes(x=reorder(feature,-missing_pct),y=missing_pct)) +
  geom_bar(stat="identity",fill="red") +
  coord_flip() + # to flip the graph
  xlab("Features") +
  ylab("Percentage of missing values")+
  scale_y_continuous(labels=scales::percent) +
  theme_ipsum()

Missing data, it is normal?

It is quite normal to see missing data in any data-set as such data is collected by manually which means that there might be some error. Missing data present various problems. First, the absence of data reduces statistical power, which refers to the probability that the test will reject the null hypothesis when it is false. Second, the lost data can cause bias in the estimation of parameters. Third, it can reduce the representativeness of the samples.

The solution to the missing data

Removal

The removal of the observations that contain missing might cause a bigger issue where it might an even bigger loss of information which cause bias in our estimation. But on close observation of our data we can identify that there are some features that are not extremely useful and contain missing data, so we can drop such features.

train <- train %>%
  select(!Cabin)
summary(train)
  PassengerId       Survived          Pclass          Name               Sex                 Age       
 Min.   :  1.0   Min.   :0.0000   Min.   :1.000   Length:891         Length:891         Min.   : 0.42  
 1st Qu.:223.5   1st Qu.:0.0000   1st Qu.:2.000   Class :character   Class :character   1st Qu.:20.12  
 Median :446.0   Median :0.0000   Median :3.000   Mode  :character   Mode  :character   Median :28.00  
 Mean   :446.0   Mean   :0.3838   Mean   :2.309                                         Mean   :29.70  
 3rd Qu.:668.5   3rd Qu.:1.0000   3rd Qu.:3.000                                         3rd Qu.:38.00  
 Max.   :891.0   Max.   :1.0000   Max.   :3.000                                         Max.   :80.00  
                                                                                        NA's   :177    
     SibSp           Parch           Ticket               Fare          Embarked        
 Min.   :0.000   Min.   :0.0000   Length:891         Min.   :  0.00   Length:891        
 1st Qu.:0.000   1st Qu.:0.0000   Class :character   1st Qu.:  7.91   Class :character  
 Median :0.000   Median :0.0000   Mode  :character   Median : 14.45   Mode  :character  
 Mean   :0.523   Mean   :0.3816                      Mean   : 32.20                     
 3rd Qu.:1.000   3rd Qu.:0.0000                      3rd Qu.: 31.00                     
 Max.   :8.000   Max.   :6.0000                      Max.   :512.33                     
                                                                                        

Imputation

Imputing the missing data gives the advantage that we use a learning model to predict such missing data and maintain the distribution of our data.

Histogram of Age feature.

gAgeDensity <- train %>%
  select(Age) %>%
  ggplot(aes(Age, y = ..density..)) +
  geom_histogram(bins = 20,binwidth = 1,color=inferno(1,alpha=1)) + 
  geom_density(fill=inferno(1,begin = 0.5,alpha = 0.5),color = inferno(1,begin=0)) + 
  annotate(
    "text",
    x = 70,
    y = 0.04,
    label = paste("Skewness:",skewness(train$Age,na.rm = T)),
    colour = inferno(1,begin = 0.1),
    size = 4
  ) + 
  theme_ipsum_rc()

gAgeDensity

Population mean and standard deviation of Age feature Before imputation

train %>%
  summarise(Age_mean = mean(Age,na.rm = T), Age_sd = sd(Age,na.rm = T))

Histogram of Fare feature

gFareDensity <- train %>%
  select(Fare) %>%
  ggplot(aes(Fare, y = ..density..)) +
  geom_histogram(bins = 20,binwidth = 1,color=viridis(1,alpha=1)) + 
  geom_density(fill=inferno(1,begin = 0.5,alpha = 0.5),color = viridis(1,begin=0)) + 
  scale_y_continuous(limits = c(0,0.05))+
  theme_ipsum_rc() + 
  annotate(
    "text",
    x = 200,
    y = 0.05,
    label = paste("Skewness",skewness(train$Fare)),
    colour = "black",
    size = 4
  )

gFareDensity 

Population mean and standard deviaiton of Fare feature.

train %>%
  summarise(Fare_mean = mean(Fare,na.rm = T), Fare_sd = sd(Fare,na.rm = T))

Imputing the missing age feature

#---------------MICE--------------
set.seed(129)
mice_mod <- mice(train[,!names(train) %in% c('PassengerId','Name','Ticket','Cabin','Survived')],method = 'rf')

 iter imp variable
  1   1  Age
  1   2  Age
  1   3  Age
  1   4  Age
  1   5  Age
  2   1  Age
  2   2  Age
  2   3  Age
  2   4  Age
  2   5  Age
  3   1  Age
  3   2  Age
  3   3  Age
  3   4  Age
  3   5  Age
  4   1  Age
  4   2  Age
  4   3  Age
  4   4  Age
  4   5  Age
  5   1  Age
  5   2  Age
  5   3  Age
  5   4  Age
  5   5  Age
Warning: Number of logged events: 2
mice_output <- complete(mice_mod)

gdistrOriginalData <- train %>%
  select(Age) %>%
  ggplot(aes(Age, y = ..density..)) +
  geom_histogram(bins = 25,binwidth = 1,color=inferno(1,alpha=1)) + 
  geom_density(fill=inferno(1,begin = 0.5,alpha = 0.5),color = inferno(1,begin=0)) +
  ggtitle("Distribution of original data") +
  theme_ipsum_rc()
gdistrMICEData <- mice_output %>%
  select(Age) %>%
  ggplot(aes(Age, y = ..density..)) +
  geom_histogram(bins = 25,binwidth = 1,color=inferno(1,alpha=1)) + 
  geom_density(fill=inferno(1,begin = 0.5,alpha = 0.5),color = inferno(1,begin=0)) + 
  ggtitle("Distribution of mice output") +
  theme_ipsum_rc()

gridExtra::grid.arrange(gdistrOriginalData,gdistrMICEData,nrow = 1)


train$Age <- mice_output$Age

missing_values <- train %>% summarize_all(funs(sum(is.na(.))/n()))

missing_values <- gather(missing_values, key="feature", value="missing_pct")

missing_values

missing_values %>% 
  ggplot(aes(x=reorder(feature,-missing_pct),y=missing_pct)) +
  geom_bar(stat="identity",fill="red") +
  coord_flip() + # to flip the graph
  xlab("Features") +
  ylab("Percentage of missing values")+
  scale_y_continuous(labels=scales::percent) +
  theme_ipsum()

Population mean and standard deviation

train %>%
  summarise(Age_mean = mean(Age,na.rm = T), Age_sd = sd(Age,na.rm = T))

Determining the distrubutuion of Age and Fare By inspection

Though determination of the distribution using inspection is likely not going to be effective we are going to the KS-test in a later section

Age

The histogram of the Age feature look very much like a normal distribution, yet it’s not a normal distribution itself.

Fare

The histogram of the Fare feature fits the chi-square distribution.

Plotting The Data

We include even more plots of our data to get a better understanding of it, help us see hidden correlations and ways to facilitate our analysis

Correlation Matrix

We are going to use correlation matrix of the numerical data to assess the correlation, which might gives a better idea of which feature might be important

correlationMatrix <- train %>%
  filter(!is.na(Age)) %>%
  select(Survived, Pclass,Age,SibSp,Parch,Fare) %>%
  cor() %>%
  ggcorrplot(lab = T,
             ggtheme =theme_ipsum_rc(grid = F),
             title="Correlation Matrix",hc.order=T,
             colors =rev(viridis(3,alpha=0.7)),
             digits = 1)

correlationMatrix

The fare features seems to be the most correlated feature to survival of the passengers, but it doesn’t negate the importance of the other features in the data. Which means that we will start by comparing the each that we consider to be important against survival feature

Class of Passenger Vs Survived

gPclassSurvived <- train %>%
  select(Pclass,Survived) %>%
  ggplot(aes(as_factor(Pclass),fill=as_factor(Survived))) + 
  geom_bar(position = "fill") +
  scale_y_continuous(labels=scales::percent) +
  theme_ipsum_rc() + 
  labs(x = "Classes",y = "Survival Rate")+
  scale_fill_discrete(name = "Survived", labels = c("Didn't Survive","Survived"))

gPclassSurvived

From this Plot, it seems clear that people from the upper classes had higher survival rates, thought this seemed obvious from the beginning. ### Siblings and Spouses Vs Survived

gSibSpSurvived <- train %>%
  select(SibSp,Survived) %>%
  ggplot(aes(as_factor(SibSp),fill=as_factor(Survived))) +
  geom_bar(position = "fill") + 
  scale_y_continuous(labels = scales::percent) +
  labs(x = "Siblings and Spouses",y = "Survival Rate")+
  scale_fill_discrete(name = "Survived", labels = c("Didn't Survive","Survived")) +
  theme_ipsum()

gSibSpSurvived

This plot highlight a point that might be counter-intuitive that people that have no siblings or spouses had lower survival rates than those with at least one sibling or a spouse

Number of children/parents Vs Survived

gParchSurvived <- train %>%
  select(Parch,Survived) %>%
  ggplot(aes(as_factor(Parch),fill=as_factor(Survived))) + 
  geom_bar(position = "fill") +
  scale_y_continuous(label = scales::percent)+
  labs(x = "Number of parents/children",y = "Survival Rate")+
  scale_fill_discrete(name = "Survived", labels = c("Didn't Survive","Survived")) +
  theme_ipsum_rc()
gParchSurvived

Gender VS Survived

gSexSurvived <- train %>%
  select(Sex,Survived) %>%
  ggplot(aes(as_factor(Sex),fill = as_factor(Survived))) + 
  geom_bar(position = "fill") +
  scale_y_continuous(label = scales::percent) + 
  labs(x = "Sex",y = "Survival Rate")+
  scale_fill_discrete(name = "Survived", labels = c("Didn't Survive","Survived")) +
  theme_ipsum_rc()
print(gSexSurvived)

Dashboard of the previous graphs

gridExtra::grid.arrange(gPclassSurvived,
                        gSibSpSurvived,
                        gSexSurvived,
                        gParchSurvived,
                        nrow=2)
train %>%
  group_by(Sex) %>%
  summarise(Age_mean = mean(Age,na.rm=TRUE),
            age_sd = sd(Age,na.rm=T),
            surival_mean = mean(Survived,na.rm =T),
            surival_sd = sd(Survived,na.rm = T))

Sampling

Random sample of size of 50 (Age)

sample_50 <- sample_n(train,size = 50,replace = T) %>%
  summarise(mean = mean(Age), sd = sd(Age))
sample_50

Sampling distrubution with a fixed size (50)

numOfSamples <- seq(50,1000,50)

smplngDstrbtnRpsChng <- tibble()

for(i in numOfSamples){
  
  for(y in 1:i){
    nsample <- sample_n(train,size=50,replace=T) %>%
      select(Age)
    newRow <- nrow(smplngDstrbtnRpsChng) + 1
    smplngDstrbtnRpsChng[newRow,"reps"] <- i
    smplngDstrbtnRpsChng[newRow,"x_bar"] <- mean(nsample$Age,na.rm = T)
  }
  
}

gSamplingReps <- smplngDstrbtnRpsChng %>%
  plot_ly(
    x = ~x_bar,
    frame = ~reps,
    type = "histogram"
  )
gSamplingReps

While no. of sample size increase the variability of sampling distribution decrease and the mean increase.

Sampling distrubution with a fixed repetitions and different sizes

sizes <- seq(20,260,20)
smplngDstrbtnSzChng <- tibble()
for(i in sizes){
  for(y in 1:1500){
    nsample <- sample_n(train,size=i,replace=T) %>%
      select(Age)
    newRow <- nrow(smplngDstrbtnSzChng) + 1
    smplngDstrbtnSzChng[newRow,"sizes"] <- i
    smplngDstrbtnSzChng[newRow,"x_bar"] <- mean(nsample$Age,na.rm = T)
  }
}
gSamplingSize <- smplngDstrbtnSzChng %>%
  plot_ly(
    x = ~x_bar,
    frame = ~sizes,
    type = "histogram"
  )
gSamplingSize

While no. of sample size increase the variability of sampling distribution decrease,and The sample distribution mean will be normally distributed as long as the sample size is more than 30.

kernel distribution

A kernel distribution is a nonparametric representation of the probability density function (\(pdf\)) of a random variable in any population

The kernel smoothing function defines the shape of the curve used to generate the pdf Kernel distribution is Quote from histogram in other word (smooth representation of a histogram) That the integral =1 There is a benefit of smooth representation of a histogram like Ignores irregularities and outliers , more efficient in approximation so it deals better with large data than small data

\[ \hat{f_h} = \frac{1}{n} = \sum^n_{i = 1} K(x-x_i) = \frac{1}{nh} K\left(\frac{x-x_i}{h}\right) \]

Rules

Non-weighted Data

\[ \hat{f_h} = \frac{1}{n} \sum^n_{i = 1} K(x-x_i) = \frac{1}{nh} K\left(\frac{x-x_i}{h}\right) \]

Weighted Data

\[ \hat{f_h} = \frac{1}{h} \sum^N_{i=1} w_i K \left(\frac{x-x_i}{h}\right), \qquad \text{where} \sum^N_{i= 1} w_i = 1 \]

Kernel Function

  1. Box
  2. Triangle
  3. Normal
  4. Pantechnicon

Each density curve uses the same input data, but applies a different kernel smoothing function to generate the pdf. The density estimates are roughly comparable, but the shape of each curve varies slightly. For example, the box kernel produces a density curve that is less smooth than the others.

The choice of bandwidth value controls the smoothness of the resulting probability density curve (higher value of \(h\) more smoothing)

Specifying a smaller bandwidth produces a very rough curve, but reveals that there might be two major peaks in the data. Specifying a larger bandwidth produces a curve nearly identical to the kernel function Choosing the optimal (\(h\)) bandwidth methods :

  1. Silverman’s rule of thump that computes an optimal h by assuming that data is normally distributed
  2. Improved Sheather Jones (ISJ) an algorithm is more robust with multimodality data or a lot of data (one disadvantage is it needs to large data )

Bounded domains data: have a constrains like data couldn’t be negative ( -ve lead to probability = 0)

Mirror method

  1. Mirror the data
  2. Sum the original and mirrored kernel density estimate
  3. Chop it so that zero at the boundary side

2D

\(h\) : could be matrix (different \(h\) in different directions)

The choice of norm comes into \(d \ge 2\)

The p-norm is \(||x||_p := (\sum_{i=1} |x|^p)^{\frac{1}{p}}\) - norm-p =1 Manhattan distance - norm-p =2 Euclidean norm - norm-p =inf maximum norm (it’s not obvious in every case which norm is the correct one)

standard euclidean distance is good choice because it invariant under rotation as large data choice of k and p isn’t important so

Answering our Questions

Acurrate summary of our data.

Q3) i)

ii)?? iii) Yes, by interval estimation iv)?? Q4) Q5)

gAgeDensity <- train %>%
  select(Age) %>%
  ggplot(aes(Age, y = ..density..)) +
  geom_histogram(bins = 20,binwidth = 1,color=inferno(1,alpha=1)) + 
  geom_density(fill=inferno(1,begin = 0.5,alpha = 0.5),color = inferno(1,begin=0)) + 
  annotate(
    "text",
    x = 70,
    y = 0.04,
    label = paste("Skewness:",skewness(train$Age,na.rm = T)),
    colour = inferno(1,begin = 0.1),
    size = 4
  ) + 
  theme_ipsum_rc()

gAgeDensity

Q6)//?

gFareDensity <- train %>%
  select(Fare) %>%
  ggplot(aes(Fare, y = ..density..)) +
  geom_histogram(bins = 20,binwidth = 1,color=viridis(1,alpha=1)) + 
  geom_density(fill=inferno(1,begin = 0.5,alpha = 0.5),color = viridis(1,begin=0)) + 
  scale_y_continuous(limits = c(0,0.05))+
  theme_ipsum_rc() + 
  annotate(
    "text",
    x = 200,
    y = 0.05,
    label = paste("Skewness",skewness(train$Fare)),
    colour = "black",
    size = 4
  )

gFareDensity

Q8) Q)9,10,11

numOfSamples <- c(50,100,1000)
smplngDstrbtonRpsChng <- tibble()
for(i in numOfSamples){
  for(y in 1:i){
    nsample <- sample_n(train,size=50,replace=T) %>%
      select(Age)
    newRow <- nrow(smplngDstrbtonRpsChng) + 1
    smplngDstrbtonRpsChng[newRow,"reps"] <- i
    smplngDstrbtonRpsChng[newRow,"x_bar"] <- mean(nsample$Age,na.rm = T)
  }
}

gSamplingReps <- smplngDstrbtonRpsChng %>%
  plot_ly(
    x = ~x_bar,
    frame = ~reps,
    type = "histogram"
  )

gSamplingReps

Q12) Q)13,14,15

Q16) Q17,18)

LS0tDQp0aXRsZTogIlRpdGFuaWMgRGF0YSBBbmFseXNpcyINCmF1dGhvcjogDQogIC0gIkFobWVkIEFzaHJhZiINCiAgLSAiT21hciBHYW1hbCBBYmRlbGF6aXoiDQogIC0gIkFobWVkIFlvdXNyaSINCiAgLSAiQWhtZWQgRGF3b29kIg0KICAtICJBbGlhIE1lZGhhdCINCm91dHB1dDoNCiAgaHRtbF9ub3RlYm9vazoNCiAgICB0b2M6IHllcw0KICBwZGZfZG9jdW1lbnQ6DQogICAgdG9jOiB5ZXMNCiAgICBudW1iZXJfc2VjdGlvbnM6IHllcw0KICAgIGluY2x1ZGVzOg0KICAgICAgaW5faGVhZGVyOiBwcmVhbWJsZS50ZXgNCiAgICBoaWdobGlnaHQ6IHB5Z21lbnRzDQphbHdheXNfYWxsb3dfaHRtbDogeWVzDQplZGl0b3Jfb3B0aW9uczogDQogIG1hcmtkb3duOiANCiAgICB3cmFwOiA3Mg0KLS0tDQoNCiMgSW50cm9kdWN0aW9uDQoNCkluIHRoaXMgcHJvamVjdCB3ZSBhcmUgdHJ5aW5nIGZpcnN0IHRvIGV4cGxvcmUgb3VyIGRhdGEgdG8gZ2V0IGEgYmV0dGVyDQp1bmRlcnN0YW5kaW5nIFRvIGFuc3dlciBvdXIgcXVlc3Rpb25zIHdlIGZpcnN0IG5lZWQgdG8gaGF2ZSBhDQpwcmVsaW1pbmFyeSBsb29rIGF0IG91ciBkYXRhLCBzbyB0aGF0IHdlIGNhbiBnZXQgYSBiZXR0ZXIgYSBpZGVhIHdoYXQgd2UNCmFyZSBkZWFsaW5nIHdpdGgsIGFzIHdlbGwgYXMgdGhlIHBvc3NpYmxlIG1pc3NpbmcgZGF0YSBhbmQgcmVsYXRpb25zaGlwcw0KdGhhdCBleGlzdA0KDQojIyBQcmVsaW1pbmFyeSBMb29rIGF0IHRoZSBkYXRhDQoNCldlIG5lZWQgZmlyc3QgdG8gZGVmaW5lIHRoZSBkYXRhIHdlIGhhdmUuDQoNCnwgVmFyaWFibGUgfCBEZWZpbml0aW9uICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBLZXkgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8Oi0tLS0tLS0tLS0tLS0tLS0tLS18Oi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS18Oi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS18DQp8IHN1cnZpdmFsIHwgU3Vydml2YWwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgMCA9IE5vLCAxID0geWVzICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfCBwY2xhc3MgICB8IHRpY2tldCBjbGFzcyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8IDEgPSAxc3QsIDIgPSAybmQsIDMgPSAzcmQgICAgICAgICAgICAgICAgICAgICAgIHwNCnwgc2V4ICAgICAgfCBzZXggICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8IGFnZSAgICAgIHwgQWdlIGluIHllYXIgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfCBzaWJzcCAgICB8IE51bWJlciBvZiBzaWJsaW5ncy9zcG91c2VzIGFib2FyZCB0aGUgdGl0YW5pYyB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwgcGFyY2ggICAgfCBOdW1iZXIgb2YgcGFyZW50cy9jaGlsZHJlbiBhYm9hcmQgdGhlIFRpdGFuaWMgfCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8IHRpY2tldCAgIHwgdGlja2V0IG51bWJlcih1bmlxdWUpICAgICAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfCBmYXJlICAgICB8IFBhc3NlbmdlciBmYXJlICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwgY2FiaW4gICAgfCBDYWJpbiBudW1iZXIgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8IGVtYmFya2VkIHwgcG9ydCBvZiBlbWJhcmthdGlvbiAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgQyA9IENoZXJib3VyZywgUSA9IFF1ZWVucy10b3duLCBTID0gU291dGhhbXB0b24gfA0KDQojIyBMb2FkaW5nIHRoZSBwYWNrYWdlcyBhbmQgdGhlIERhdGENCg0KYGBge3IgTG9hZGluZyBQYWNrYWdlcywgZWNobz1UUlVFLCBtZXNzYWdlPUZBTFNFLCByZXN1bHRzPSdoaWRlJ30NCiMgTG9hZGluZyBQYWNrYWdlcw0KIyMgdGlkeXZlcnNlIGxvYWRzIGRwbHlyIGFuZCByZWFkcg0KbGlicmFyeSh0aWR5dmVyc2UpDQoNCiMjIFRvIGhhdmUgZGlmZmVyZW50IGNvbG9yIG1hcHMNCmxpYnJhcnkodmlyaWRpcykNCg0KIyMgZ2dwbG90MiB0byBwcm9kdWNlIGRpZmZlcmVudCBwbG90cw0KbGlicmFyeShnZ3Bsb3QyKQ0KDQojIyB1c2VzIGdncGxvdDIgdG8gcHJvZHVjZSBhIGNvcnJlbGF0aW9uIG1hdHJpeCAtLSB0aGUgZGF0YSBtdXN0IGJlIGluIHRoZSBjb3JyZWN0IGZvcm0NCmxpYnJhcnkoZ2djb3JycGxvdCkNCg0KIyMgR2l2ZXMgdXMgYmV0dGVyIHRoZW1lcw0KbGlicmFyeShocmJydGhlbWVzKQ0KDQojIyB0byB1c2Ugc2tld25lc3MgZnVuLiB0byBjYWxjdWxhdGUgc2tld25lc3Mgb2YgdGhlIGRpc3RyaWJ1dGlvbg0KbGlicmFyeShlMTA3MSkNCg0KIyMgTXVsdGl2YXJpYXRlIGltcHV0YXRpb24gdXNpbmcgY2hhaW5lZCBlcXVhdGlvbnMgLS0gdG8gaW1wdXRlIHRoZSBtaXNzaW5nIHZhbHVlcyBpbiBvdXIgZGF0YQ0KbGlicmFyeShtaWNlKQ0KDQojIyBMb2FkcyBkaWZmZXJlbnQgc3RhdGlzdGljYWwgZnVuY3Rpb25zDQpsaWJyYXJ5KHN0YXRzcikNCg0KIyMgVG8gcHJvZHVjZSBpbnRlcmFjdGl2ZSBwbG90DQpsaWJyYXJ5KHBsb3RseSkNCg0KDQojIExvYWRpbmcgVHJhaW5pbmcgRGF0YQ0KdHJhaW4gPC0gcmVhZF9jc3YoImRhdGEvdHJhaW4uY3N2IikNCg0KIyBMb2FkaW5nIFRlc3RpbmcgRGF0YQ0KdGVzdCA8LSByZWFkX2NzdigiZGF0YS90ZXN0LmNzdiIpDQoNCiMgQmluZGluZyB0aGVtIGludG8gYSBmdWxsIGRhdGEgZnJhbWUNCmRmIDwtIGJpbmRfcm93cyh0cmFpbix0ZXN0KQ0KYGBgDQoNCiMgRXhwbG9yYXRpb24gT2YgVGhlIERhdGENCg0KIyMgRGVzY3JpcHRpb24gb2YgdGhlIERhdGENCg0KIyMgU3VtbWFyeSBvZiBEYXRhDQoNCmBgYHtyIERhdGEgU3VtbWFyeX0NCnN1bW1hcnkodHJhaW4pDQpgYGANCg0KIyMgQ2F0ZWdvcmllcyBvZiBGZWF0dXJlcw0KDQo+IFF1YW50aXRhdGl2ZSBkYXRhIGFyZSBtZWFzdXJlcyBvZiB2YWx1ZXMgb3IgY291bnRzIGFuZCBhcmUgZXhwcmVzc2VkDQo+IGFzIG51bWJlcnMuDQoNCj4gUXVhbGl0YXRpdmUgZGF0YSBhcmUgbWVhc3VyZXMgb2YgJ3R5cGVzJyBhbmQgbWF5IGJlIHJlcHJlc2VudGVkIGJ5IGENCj4gbmFtZSwgc3ltYm9sLCBvciBhIG51bWJlciBjb2RlLg0KDQojIyMgUXVhbGl0aXZlDQoNCkNhdGVnb3JpY2FsOiBTdXJ2aXZlZCwgU2V4LCBhbmQgRW1iYXJrZWQuIE9yZGluYWw6IFBjbGFzcy4gTm9taW5hbDoNCk5hbWUuDQoNCiMjIyBRdWFudGl0aXZlDQoNCkNvbnRpbnVvdXM6IEFnZSwgRmFyZS4gRGlzY3JldGU6IFNpYlNwLCBQYXJjaC4NCg0KIyMjIE1peCB0eXBlcw0KDQpUaWNrZXQgaXMgYSBtaXggb2YgbnVtZXJpYyBhbmQgYWxwaGFudW1lcmljIGRhdGEgdHlwZXMgQ2FiaW4gaXMgbWl4DQpiZXR3ZWVuIGFscGhhIGFuZCBudW1lcmljDQoNCiMjIEV4cGxvcmluZyBNaXNzaW5nIERhdGEuDQoNCkNoZWNraW5nIGZvciBNaXNzaW5nIHZhbHVlcyBpbiBlYWNoIGZlYXR1cmUNCg0KYGBge3IgTWlzc2luZyBWYWx1ZXN9DQpjb2xTdW1zKGlzLm5hKHRyYWluKSkNCmBgYA0KDQpgYGB7ciBNaXNzaW5nIHZhbHVlcyBwbG90fQ0KbWlzc2luZ192YWx1ZXMgPC0gdHJhaW4gJT4lIHN1bW1hcml6ZV9hbGwoZnVucyhzdW0oaXMubmEoLikpL24oKSkpDQoNCm1pc3NpbmdfdmFsdWVzIDwtIGdhdGhlcihtaXNzaW5nX3ZhbHVlcywga2V5PSJmZWF0dXJlIiwgdmFsdWU9Im1pc3NpbmdfcGN0IikNCg0KbWlzc2luZ192YWx1ZXMNCg0KbWlzc2luZ192YWx1ZXMgJT4lIA0KICBnZ3Bsb3QoYWVzKHg9cmVvcmRlcihmZWF0dXJlLC1taXNzaW5nX3BjdCkseT1taXNzaW5nX3BjdCkpICsNCiAgZ2VvbV9iYXIoc3RhdD0iaWRlbnRpdHkiLGZpbGw9InJlZCIpICsNCiAgY29vcmRfZmxpcCgpICsgIyB0byBmbGlwIHRoZSBncmFwaA0KICB4bGFiKCJGZWF0dXJlcyIpICsNCiAgeWxhYigiUGVyY2VudGFnZSBvZiBtaXNzaW5nIHZhbHVlcyIpKw0KICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzPXNjYWxlczo6cGVyY2VudCkgKw0KICB0aGVtZV9pcHN1bSgpDQpgYGANCg0KIyMjIE1pc3NpbmcgZGF0YSwgaXQgaXMgbm9ybWFsPw0KDQpJdCBpcyBxdWl0ZSBub3JtYWwgdG8gc2VlIG1pc3NpbmcgZGF0YSBpbiBhbnkgZGF0YS1zZXQgYXMgc3VjaCBkYXRhIGlzDQpjb2xsZWN0ZWQgYnkgbWFudWFsbHkgd2hpY2ggbWVhbnMgdGhhdCB0aGVyZSBtaWdodCBiZSBzb21lIGVycm9yLg0KTWlzc2luZyBkYXRhIHByZXNlbnQgdmFyaW91cyBwcm9ibGVtcy4gRmlyc3QsIHRoZSBhYnNlbmNlIG9mIGRhdGENCnJlZHVjZXMgc3RhdGlzdGljYWwgcG93ZXIsIHdoaWNoIHJlZmVycyB0byB0aGUgcHJvYmFiaWxpdHkgdGhhdCB0aGUgdGVzdA0Kd2lsbCByZWplY3QgdGhlIG51bGwgaHlwb3RoZXNpcyB3aGVuIGl0IGlzIGZhbHNlLiBTZWNvbmQsIHRoZSBsb3N0IGRhdGENCmNhbiBjYXVzZSBiaWFzIGluIHRoZSBlc3RpbWF0aW9uIG9mIHBhcmFtZXRlcnMuIFRoaXJkLCBpdCBjYW4gcmVkdWNlIHRoZQ0KcmVwcmVzZW50YXRpdmVuZXNzIG9mIHRoZSBzYW1wbGVzLg0KDQojIyMgVGhlIHNvbHV0aW9uIHRvIHRoZSBtaXNzaW5nIGRhdGENCg0KIyMjIyBSZW1vdmFsDQoNClRoZSByZW1vdmFsIG9mIHRoZSBvYnNlcnZhdGlvbnMgdGhhdCBjb250YWluIG1pc3NpbmcgbWlnaHQgY2F1c2UgYQ0KYmlnZ2VyIGlzc3VlIHdoZXJlIGl0IG1pZ2h0IGFuIGV2ZW4gYmlnZ2VyIGxvc3Mgb2YgaW5mb3JtYXRpb24gd2hpY2gNCmNhdXNlIGJpYXMgaW4gb3VyIGVzdGltYXRpb24uIEJ1dCBvbiBjbG9zZSBvYnNlcnZhdGlvbiBvZiBvdXIgZGF0YSB3ZQ0KY2FuIGlkZW50aWZ5IHRoYXQgdGhlcmUgYXJlIHNvbWUgZmVhdHVyZXMgdGhhdCBhcmUgbm90IGV4dHJlbWVseSB1c2VmdWwNCmFuZCBjb250YWluIG1pc3NpbmcgZGF0YSwgc28gd2UgY2FuIGRyb3Agc3VjaCBmZWF0dXJlcy4NCg0KYGBge3IgZHJvcGluZyBjYWJpbn0NCnRyYWluIDwtIHRyYWluICU+JQ0KICBzZWxlY3QoIUNhYmluKQ0Kc3VtbWFyeSh0cmFpbikNCmBgYA0KDQojIyMjIEltcHV0YXRpb24NCg0KSW1wdXRpbmcgdGhlIG1pc3NpbmcgZGF0YSBnaXZlcyB0aGUgYWR2YW50YWdlIHRoYXQgd2UgdXNlIGEgbGVhcm5pbmcNCm1vZGVsIHRvIHByZWRpY3Qgc3VjaCBtaXNzaW5nIGRhdGEgYW5kIG1haW50YWluIHRoZSBkaXN0cmlidXRpb24gb2Ygb3VyDQpkYXRhLg0KDQojIyBIaXN0b2dyYW0gb2YgQWdlIGZlYXR1cmUuDQoNCmBgYHtyIEFnZSBoaXN0b2dyYW19DQpnQWdlRGVuc2l0eSA8LSB0cmFpbiAlPiUNCiAgc2VsZWN0KEFnZSkgJT4lDQogIGdncGxvdChhZXMoQWdlLCB5ID0gLi5kZW5zaXR5Li4pKSArDQogIGdlb21faGlzdG9ncmFtKGJpbnMgPSAyMCxiaW53aWR0aCA9IDEsY29sb3I9aW5mZXJubygxLGFscGhhPTEpKSArIA0KICBnZW9tX2RlbnNpdHkoZmlsbD1pbmZlcm5vKDEsYmVnaW4gPSAwLjUsYWxwaGEgPSAwLjUpLGNvbG9yID0gaW5mZXJubygxLGJlZ2luPTApKSArIA0KICBhbm5vdGF0ZSgNCiAgICAidGV4dCIsDQogICAgeCA9IDcwLA0KICAgIHkgPSAwLjA0LA0KICAgIGxhYmVsID0gcGFzdGUoIlNrZXduZXNzOiIsc2tld25lc3ModHJhaW4kQWdlLG5hLnJtID0gVCkpLA0KICAgIGNvbG91ciA9IGluZmVybm8oMSxiZWdpbiA9IDAuMSksDQogICAgc2l6ZSA9IDQNCiAgKSArIA0KICB0aGVtZV9pcHN1bV9yYygpDQoNCmdBZ2VEZW5zaXR5DQpgYGANCg0KIyMjIFBvcHVsYXRpb24gbWVhbiBhbmQgc3RhbmRhcmQgZGV2aWF0aW9uIG9mIEFnZSBmZWF0dXJlIEJlZm9yZSBpbXB1dGF0aW9uDQoNCmBgYHtyIFN1bW1hcnkgb2YgQWdlIGZlYXR1cmV9DQp0cmFpbiAlPiUNCiAgc3VtbWFyaXNlKEFnZV9tZWFuID0gbWVhbihBZ2UsbmEucm0gPSBUKSwgQWdlX3NkID0gc2QoQWdlLG5hLnJtID0gVCkpDQpgYGANCg0KIyMgSGlzdG9ncmFtIG9mIEZhcmUgZmVhdHVyZQ0KDQpgYGB7ciBIaXN0b2dyYW0gb2YgRmFyZSBGZWF0dXJlfQ0KZ0ZhcmVEZW5zaXR5IDwtIHRyYWluICU+JQ0KICBzZWxlY3QoRmFyZSkgJT4lDQogIGdncGxvdChhZXMoRmFyZSwgeSA9IC4uZGVuc2l0eS4uKSkgKw0KICBnZW9tX2hpc3RvZ3JhbShiaW5zID0gMjAsYmlud2lkdGggPSAxLGNvbG9yPXZpcmlkaXMoMSxhbHBoYT0xKSkgKyANCiAgZ2VvbV9kZW5zaXR5KGZpbGw9aW5mZXJubygxLGJlZ2luID0gMC41LGFscGhhID0gMC41KSxjb2xvciA9IHZpcmlkaXMoMSxiZWdpbj0wKSkgKyANCiAgc2NhbGVfeV9jb250aW51b3VzKGxpbWl0cyA9IGMoMCwwLjA1KSkrDQogIHRoZW1lX2lwc3VtX3JjKCkgKyANCiAgYW5ub3RhdGUoDQogICAgInRleHQiLA0KICAgIHggPSAyMDAsDQogICAgeSA9IDAuMDUsDQogICAgbGFiZWwgPSBwYXN0ZSgiU2tld25lc3MiLHNrZXduZXNzKHRyYWluJEZhcmUpKSwNCiAgICBjb2xvdXIgPSAiYmxhY2siLA0KICAgIHNpemUgPSA0DQogICkNCg0KZ0ZhcmVEZW5zaXR5IA0KYGBgDQoNCiMjIyBQb3B1bGF0aW9uIG1lYW4gYW5kIHN0YW5kYXJkIGRldmlhaXRvbiBvZiBGYXJlIGZlYXR1cmUuDQoNCmBgYHtyIFN1bW1hcnkgb2YgRmFyZSBGZWF0dXJlfQ0KdHJhaW4gJT4lDQogIHN1bW1hcmlzZShGYXJlX21lYW4gPSBtZWFuKEZhcmUsbmEucm0gPSBUKSwgRmFyZV9zZCA9IHNkKEZhcmUsbmEucm0gPSBUKSkNCmBgYA0KDQojIyBJbXB1dGluZyB0aGUgbWlzc2luZyBhZ2UgZmVhdHVyZQ0KDQpgYGB7ciBJbXB1dGF0aW9ufQ0KIy0tLS0tLS0tLS0tLS0tLU1JQ0UtLS0tLS0tLS0tLS0tLQ0Kc2V0LnNlZWQoMTI5KQ0KbWljZV9tb2QgPC0gbWljZSh0cmFpblssIW5hbWVzKHRyYWluKSAlaW4lIGMoJ1Bhc3NlbmdlcklkJywnTmFtZScsJ1RpY2tldCcsJ0NhYmluJywnU3Vydml2ZWQnKV0sbWV0aG9kID0gJ3JmJykNCm1pY2Vfb3V0cHV0IDwtIGNvbXBsZXRlKG1pY2VfbW9kKQ0KDQpnZGlzdHJPcmlnaW5hbERhdGEgPC0gdHJhaW4gJT4lDQogIHNlbGVjdChBZ2UpICU+JQ0KICBnZ3Bsb3QoYWVzKEFnZSwgeSA9IC4uZGVuc2l0eS4uKSkgKw0KICBnZW9tX2hpc3RvZ3JhbShiaW5zID0gMjUsYmlud2lkdGggPSAxLGNvbG9yPWluZmVybm8oMSxhbHBoYT0xKSkgKyANCiAgZ2VvbV9kZW5zaXR5KGZpbGw9aW5mZXJubygxLGJlZ2luID0gMC41LGFscGhhID0gMC41KSxjb2xvciA9IGluZmVybm8oMSxiZWdpbj0wKSkgKw0KICBnZ3RpdGxlKCJEaXN0cmlidXRpb24gb2Ygb3JpZ2luYWwgZGF0YSIpICsNCiAgdGhlbWVfaXBzdW1fcmMoKQ0KZ2Rpc3RyTUlDRURhdGEgPC0gbWljZV9vdXRwdXQgJT4lDQogIHNlbGVjdChBZ2UpICU+JQ0KICBnZ3Bsb3QoYWVzKEFnZSwgeSA9IC4uZGVuc2l0eS4uKSkgKw0KICBnZW9tX2hpc3RvZ3JhbShiaW5zID0gMjUsYmlud2lkdGggPSAxLGNvbG9yPWluZmVybm8oMSxhbHBoYT0xKSkgKyANCiAgZ2VvbV9kZW5zaXR5KGZpbGw9aW5mZXJubygxLGJlZ2luID0gMC41LGFscGhhID0gMC41KSxjb2xvciA9IGluZmVybm8oMSxiZWdpbj0wKSkgKyANCiAgZ2d0aXRsZSgiRGlzdHJpYnV0aW9uIG9mIG1pY2Ugb3V0cHV0IikgKw0KICB0aGVtZV9pcHN1bV9yYygpDQoNCmdyaWRFeHRyYTo6Z3JpZC5hcnJhbmdlKGdkaXN0ck9yaWdpbmFsRGF0YSxnZGlzdHJNSUNFRGF0YSxucm93ID0gMSkNCg0KdHJhaW4kQWdlIDwtIG1pY2Vfb3V0cHV0JEFnZQ0KDQptaXNzaW5nX3ZhbHVlcyA8LSB0cmFpbiAlPiUgc3VtbWFyaXplX2FsbChmdW5zKHN1bShpcy5uYSguKSkvbigpKSkNCg0KbWlzc2luZ192YWx1ZXMgPC0gZ2F0aGVyKG1pc3NpbmdfdmFsdWVzLCBrZXk9ImZlYXR1cmUiLCB2YWx1ZT0ibWlzc2luZ19wY3QiKQ0KDQptaXNzaW5nX3ZhbHVlcw0KDQptaXNzaW5nX3ZhbHVlcyAlPiUgDQogIGdncGxvdChhZXMoeD1yZW9yZGVyKGZlYXR1cmUsLW1pc3NpbmdfcGN0KSx5PW1pc3NpbmdfcGN0KSkgKw0KICBnZW9tX2JhcihzdGF0PSJpZGVudGl0eSIsZmlsbD0icmVkIikgKw0KICBjb29yZF9mbGlwKCkgKyAjIHRvIGZsaXAgdGhlIGdyYXBoDQogIHhsYWIoIkZlYXR1cmVzIikgKw0KICB5bGFiKCJQZXJjZW50YWdlIG9mIG1pc3NpbmcgdmFsdWVzIikrDQogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHM9c2NhbGVzOjpwZXJjZW50KSArDQogIHRoZW1lX2lwc3VtKCkNCmBgYA0KDQojIyMgUG9wdWxhdGlvbiBtZWFuIGFuZCBzdGFuZGFyZCBkZXZpYXRpb24NCg0KYGBge3IgU3VtbWFyeSBvZiBBZ2UgZmVhdHVyZSBBZnRlciBJbXB1dGF0aW9ufQ0KdHJhaW4gJT4lDQogIHN1bW1hcmlzZShBZ2VfbWVhbiA9IG1lYW4oQWdlLG5hLnJtID0gVCksIEFnZV9zZCA9IHNkKEFnZSxuYS5ybSA9IFQpKQ0KYGBgDQoNCiMjIERldGVybWluaW5nIHRoZSBkaXN0cnVidXR1aW9uIG9mIEFnZSBhbmQgRmFyZSBCeSBpbnNwZWN0aW9uDQoNClRob3VnaCBkZXRlcm1pbmF0aW9uIG9mIHRoZSBkaXN0cmlidXRpb24gdXNpbmcgaW5zcGVjdGlvbiBpcyBsaWtlbHkgbm90DQpnb2luZyB0byBiZSBlZmZlY3RpdmUgd2UgYXJlIGdvaW5nIHRvIHRoZSBLUy10ZXN0IGluIGEgbGF0ZXIgc2VjdGlvbg0KDQojIyMgQWdlDQoNClRoZSBoaXN0b2dyYW0gb2YgdGhlIEFnZSBmZWF0dXJlIGxvb2sgdmVyeSBtdWNoIGxpa2UgYSBub3JtYWwNCmRpc3RyaWJ1dGlvbiwgeWV0IGl0J3Mgbm90IGEgbm9ybWFsIGRpc3RyaWJ1dGlvbiBpdHNlbGYuDQoNCiMjIyBGYXJlDQoNClRoZSBoaXN0b2dyYW0gb2YgdGhlIEZhcmUgZmVhdHVyZSBmaXRzIHRoZSBjaGktc3F1YXJlIGRpc3RyaWJ1dGlvbi4NCg0KIyMgUGxvdHRpbmcgVGhlIERhdGENCg0KV2UgaW5jbHVkZSBldmVuIG1vcmUgcGxvdHMgb2Ygb3VyIGRhdGEgdG8gZ2V0IGEgYmV0dGVyIHVuZGVyc3RhbmRpbmcgb2YNCml0LCBoZWxwIHVzIHNlZSBoaWRkZW4gY29ycmVsYXRpb25zIGFuZCB3YXlzIHRvIGZhY2lsaXRhdGUgb3VyIGFuYWx5c2lzDQoNCiMjIyBDb3JyZWxhdGlvbiBNYXRyaXgNCg0KV2UgYXJlIGdvaW5nIHRvIHVzZSBjb3JyZWxhdGlvbiBtYXRyaXggb2YgdGhlIG51bWVyaWNhbCBkYXRhIHRvIGFzc2Vzcw0KdGhlIGNvcnJlbGF0aW9uLCB3aGljaCBtaWdodCBnaXZlcyBhIGJldHRlciBpZGVhIG9mIHdoaWNoIGZlYXR1cmUgbWlnaHQNCmJlIGltcG9ydGFudA0KDQpgYGB7ciBDb3JyZWxhdGlvbiBNYXRyaXgsIGZpZy5hbGlnbj0nY2VudGVyJ30NCmNvcnJlbGF0aW9uTWF0cml4IDwtIHRyYWluICU+JQ0KICBmaWx0ZXIoIWlzLm5hKEFnZSkpICU+JQ0KICBzZWxlY3QoU3Vydml2ZWQsIFBjbGFzcyxBZ2UsU2liU3AsUGFyY2gsRmFyZSkgJT4lDQogIGNvcigpICU+JQ0KICBnZ2NvcnJwbG90KGxhYiA9IFQsDQogICAgICAgICAgICAgZ2d0aGVtZSA9dGhlbWVfaXBzdW1fcmMoZ3JpZCA9IEYpLA0KICAgICAgICAgICAgIHRpdGxlPSJDb3JyZWxhdGlvbiBNYXRyaXgiLGhjLm9yZGVyPVQsDQogICAgICAgICAgICAgY29sb3JzID1yZXYodmlyaWRpcygzLGFscGhhPTAuNykpLA0KICAgICAgICAgICAgIGRpZ2l0cyA9IDEpDQoNCmNvcnJlbGF0aW9uTWF0cml4DQpgYGANCg0KVGhlIGZhcmUgZmVhdHVyZXMgc2VlbXMgdG8gYmUgdGhlIG1vc3QgY29ycmVsYXRlZCBmZWF0dXJlIHRvIHN1cnZpdmFsIG9mDQp0aGUgcGFzc2VuZ2VycywgYnV0IGl0IGRvZXNuJ3QgbmVnYXRlIHRoZSBpbXBvcnRhbmNlIG9mIHRoZSBvdGhlcg0KZmVhdHVyZXMgaW4gdGhlIGRhdGEuIFdoaWNoIG1lYW5zIHRoYXQgd2Ugd2lsbCBzdGFydCBieSBjb21wYXJpbmcgdGhlDQplYWNoIHRoYXQgd2UgY29uc2lkZXIgdG8gYmUgaW1wb3J0YW50IGFnYWluc3Qgc3Vydml2YWwgZmVhdHVyZQ0KDQojIyMgQ2xhc3Mgb2YgUGFzc2VuZ2VyIFZzIFN1cnZpdmVkDQoNCmBgYHtyIFBjbGFzc1ZTU3Vydml2ZWQsZmlnLmFsaWduPSdjZW50ZXInfQ0KZ1BjbGFzc1N1cnZpdmVkIDwtIHRyYWluICU+JQ0KICBzZWxlY3QoUGNsYXNzLFN1cnZpdmVkKSAlPiUNCiAgZ2dwbG90KGFlcyhhc19mYWN0b3IoUGNsYXNzKSxmaWxsPWFzX2ZhY3RvcihTdXJ2aXZlZCkpKSArIA0KICBnZW9tX2Jhcihwb3NpdGlvbiA9ICJmaWxsIikgKw0KICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzPXNjYWxlczo6cGVyY2VudCkgKw0KICB0aGVtZV9pcHN1bV9yYygpICsgDQogIGxhYnMoeCA9ICJDbGFzc2VzIix5ID0gIlN1cnZpdmFsIFJhdGUiKSsNCiAgc2NhbGVfZmlsbF9kaXNjcmV0ZShuYW1lID0gIlN1cnZpdmVkIiwgbGFiZWxzID0gYygiRGlkbid0IFN1cnZpdmUiLCJTdXJ2aXZlZCIpKQ0KDQpnUGNsYXNzU3Vydml2ZWQNCmBgYA0KDQpGcm9tIHRoaXMgUGxvdCwgaXQgc2VlbXMgY2xlYXIgdGhhdCBwZW9wbGUgZnJvbSB0aGUgdXBwZXIgY2xhc3NlcyBoYWQgaGlnaGVyIHN1cnZpdmFsIHJhdGVzLCB0aG91Z2h0IHRoaXMgc2VlbWVkIG9idmlvdXMgZnJvbSB0aGUgYmVnaW5uaW5nLg0KIyMjIFNpYmxpbmdzIGFuZCBTcG91c2VzIFZzIFN1cnZpdmVkDQoNCmBgYHtyIFNpYlNwVlNTdXJ2aXZlZCxmaWcuYWxpZ249J2NlbnRlcid9DQpnU2liU3BTdXJ2aXZlZCA8LSB0cmFpbiAlPiUNCiAgc2VsZWN0KFNpYlNwLFN1cnZpdmVkKSAlPiUNCiAgZ2dwbG90KGFlcyhhc19mYWN0b3IoU2liU3ApLGZpbGw9YXNfZmFjdG9yKFN1cnZpdmVkKSkpICsNCiAgZ2VvbV9iYXIocG9zaXRpb24gPSAiZmlsbCIpICsgDQogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBzY2FsZXM6OnBlcmNlbnQpICsNCiAgbGFicyh4ID0gIlNpYmxpbmdzIGFuZCBTcG91c2VzIix5ID0gIlN1cnZpdmFsIFJhdGUiKSsNCiAgc2NhbGVfZmlsbF9kaXNjcmV0ZShuYW1lID0gIlN1cnZpdmVkIiwgbGFiZWxzID0gYygiRGlkbid0IFN1cnZpdmUiLCJTdXJ2aXZlZCIpKSArDQogIHRoZW1lX2lwc3VtKCkNCg0KZ1NpYlNwU3Vydml2ZWQNCmBgYA0KDQpUaGlzIHBsb3QgaGlnaGxpZ2h0IGEgcG9pbnQgdGhhdCBtaWdodCBiZSBjb3VudGVyLWludHVpdGl2ZSB0aGF0IHBlb3BsZSB0aGF0IGhhdmUgbm8gc2libGluZ3Mgb3Igc3BvdXNlcyBoYWQgbG93ZXIgc3Vydml2YWwgcmF0ZXMgdGhhbiB0aG9zZSB3aXRoIGF0IGxlYXN0IG9uZSBzaWJsaW5nIG9yIGEgc3BvdXNlDQoNCiMjIyBOdW1iZXIgb2YgY2hpbGRyZW4vcGFyZW50cyBWcyBTdXJ2aXZlZA0KDQpgYGB7ciBmaWcuYWxpZ249J2NlbnRlcid9DQpnUGFyY2hTdXJ2aXZlZCA8LSB0cmFpbiAlPiUNCiAgc2VsZWN0KFBhcmNoLFN1cnZpdmVkKSAlPiUNCiAgZ2dwbG90KGFlcyhhc19mYWN0b3IoUGFyY2gpLGZpbGw9YXNfZmFjdG9yKFN1cnZpdmVkKSkpICsgDQogIGdlb21fYmFyKHBvc2l0aW9uID0gImZpbGwiKSArDQogIHNjYWxlX3lfY29udGludW91cyhsYWJlbCA9IHNjYWxlczo6cGVyY2VudCkrDQogIGxhYnMoeCA9ICJOdW1iZXIgb2YgcGFyZW50cy9jaGlsZHJlbiIseSA9ICJTdXJ2aXZhbCBSYXRlIikrDQogIHNjYWxlX2ZpbGxfZGlzY3JldGUobmFtZSA9ICJTdXJ2aXZlZCIsIGxhYmVscyA9IGMoIkRpZG4ndCBTdXJ2aXZlIiwiU3Vydml2ZWQiKSkgKw0KICB0aGVtZV9pcHN1bV9yYygpDQpnUGFyY2hTdXJ2aXZlZA0KYGBgDQoNCiMjIyBHZW5kZXIgVlMgU3Vydml2ZWQNCg0KYGBge3IgZmlnLmFsaWduPSdjZW50ZXInfQ0KZ1NleFN1cnZpdmVkIDwtIHRyYWluICU+JQ0KICBzZWxlY3QoU2V4LFN1cnZpdmVkKSAlPiUNCiAgZ2dwbG90KGFlcyhhc19mYWN0b3IoU2V4KSxmaWxsID0gYXNfZmFjdG9yKFN1cnZpdmVkKSkpICsgDQogIGdlb21fYmFyKHBvc2l0aW9uID0gImZpbGwiKSArDQogIHNjYWxlX3lfY29udGludW91cyhsYWJlbCA9IHNjYWxlczo6cGVyY2VudCkgKyANCiAgbGFicyh4ID0gIlNleCIseSA9ICJTdXJ2aXZhbCBSYXRlIikrDQogIHNjYWxlX2ZpbGxfZGlzY3JldGUobmFtZSA9ICJTdXJ2aXZlZCIsIGxhYmVscyA9IGMoIkRpZG4ndCBTdXJ2aXZlIiwiU3Vydml2ZWQiKSkgKw0KICB0aGVtZV9pcHN1bV9yYygpDQpwcmludChnU2V4U3Vydml2ZWQpDQpgYGANCg0KIyMjIERhc2hib2FyZCBvZiB0aGUgcHJldmlvdXMgZ3JhcGhzDQoNCmBgYHtyfQ0KZ3JpZEV4dHJhOjpncmlkLmFycmFuZ2UoZ1BjbGFzc1N1cnZpdmVkLA0KICAgICAgICAgICAgICAgICAgICAgICAgZ1NpYlNwU3Vydml2ZWQsDQogICAgICAgICAgICAgICAgICAgICAgICBnU2V4U3Vydml2ZWQsDQogICAgICAgICAgICAgICAgICAgICAgICBnUGFyY2hTdXJ2aXZlZCwNCiAgICAgICAgICAgICAgICAgICAgICAgIG5yb3c9MikNCmBgYA0KDQpgYGB7cn0NCnRyYWluICU+JQ0KICBncm91cF9ieShTZXgpICU+JQ0KICBzdW1tYXJpc2UoQWdlX21lYW4gPSBtZWFuKEFnZSxuYS5ybT1UUlVFKSwNCiAgICAgICAgICAgIGFnZV9zZCA9IHNkKEFnZSxuYS5ybT1UKSwNCiAgICAgICAgICAgIHN1cml2YWxfbWVhbiA9IG1lYW4oU3Vydml2ZWQsbmEucm0gPVQpLA0KICAgICAgICAgICAgc3VyaXZhbF9zZCA9IHNkKFN1cnZpdmVkLG5hLnJtID0gVCkpDQoNCmBgYA0KDQojIyBTYW1wbGluZw0KDQojIyMgUmFuZG9tIHNhbXBsZSBvZiBzaXplIG9mIDUwIChBZ2UpDQoNCmBgYHtyIFNhbXBsaW5nLTF9DQpzYW1wbGVfNTAgPC0gc2FtcGxlX24odHJhaW4sc2l6ZSA9IDUwLHJlcGxhY2UgPSBUKSAlPiUNCiAgc3VtbWFyaXNlKG1lYW4gPSBtZWFuKEFnZSksIHNkID0gc2QoQWdlKSkNCnNhbXBsZV81MA0KYGBgDQojIyMgU2FtcGxpbmcgZGlzdHJ1YnV0aW9uIHdpdGggYSBmaXhlZCBzaXplICg1MCkNCmBgYHtyfQ0KbnVtT2ZTYW1wbGVzIDwtIHNlcSg1MCwxMDAwLDUwKQ0KDQpzbXBsbmdEc3RyYnRuUnBzQ2huZyA8LSB0aWJibGUoKQ0KDQpmb3IoaSBpbiBudW1PZlNhbXBsZXMpew0KICANCiAgZm9yKHkgaW4gMTppKXsNCiAgICBuc2FtcGxlIDwtIHNhbXBsZV9uKHRyYWluLHNpemU9NTAscmVwbGFjZT1UKSAlPiUNCiAgICAgIHNlbGVjdChBZ2UpDQogICAgbmV3Um93IDwtIG5yb3coc21wbG5nRHN0cmJ0blJwc0NobmcpICsgMQ0KICAgIHNtcGxuZ0RzdHJidG5ScHNDaG5nW25ld1JvdywicmVwcyJdIDwtIGkNCiAgICBzbXBsbmdEc3RyYnRuUnBzQ2huZ1tuZXdSb3csInhfYmFyIl0gPC0gbWVhbihuc2FtcGxlJEFnZSxuYS5ybSA9IFQpDQogIH0NCiAgDQp9DQoNCmdTYW1wbGluZ1JlcHMgPC0gc21wbG5nRHN0cmJ0blJwc0NobmcgJT4lDQogIHBsb3RfbHkoDQogICAgeCA9IH54X2JhciwNCiAgICBmcmFtZSA9IH5yZXBzLA0KICAgIHR5cGUgPSAiaGlzdG9ncmFtIg0KICApDQpnU2FtcGxpbmdSZXBzDQpgYGANCldoaWxlIG5vLiBvZiBzYW1wbGUgc2l6ZSBpbmNyZWFzZSB0aGUgdmFyaWFiaWxpdHkgb2Ygc2FtcGxpbmcNCmRpc3RyaWJ1dGlvbiBkZWNyZWFzZSBhbmQgdGhlIG1lYW4gaW5jcmVhc2UuDQoNCiMjIyBTYW1wbGluZyBkaXN0cnVidXRpb24gd2l0aCBhIGZpeGVkIHJlcGV0aXRpb25zIGFuZCBkaWZmZXJlbnQgc2l6ZXMNCg0KYGBge3Igc2FtcGxpbmctZGlzdHJ1YnV0aW9uLXNpemVzfQ0Kc2l6ZXMgPC0gc2VxKDIwLDI2MCwyMCkNCnNtcGxuZ0RzdHJidG5TekNobmcgPC0gdGliYmxlKCkNCmZvcihpIGluIHNpemVzKXsNCiAgZm9yKHkgaW4gMToxNTAwKXsNCiAgICBuc2FtcGxlIDwtIHNhbXBsZV9uKHRyYWluLHNpemU9aSxyZXBsYWNlPVQpICU+JQ0KICAgICAgc2VsZWN0KEFnZSkNCiAgICBuZXdSb3cgPC0gbnJvdyhzbXBsbmdEc3RyYnRuU3pDaG5nKSArIDENCiAgICBzbXBsbmdEc3RyYnRuU3pDaG5nW25ld1Jvdywic2l6ZXMiXSA8LSBpDQogICAgc21wbG5nRHN0cmJ0blN6Q2huZ1tuZXdSb3csInhfYmFyIl0gPC0gbWVhbihuc2FtcGxlJEFnZSxuYS5ybSA9IFQpDQogIH0NCn0NCmdTYW1wbGluZ1NpemUgPC0gc21wbG5nRHN0cmJ0blN6Q2huZyAlPiUNCiAgcGxvdF9seSgNCiAgICB4ID0gfnhfYmFyLA0KICAgIGZyYW1lID0gfnNpemVzLA0KICAgIHR5cGUgPSAiaGlzdG9ncmFtIg0KICApDQpnU2FtcGxpbmdTaXplDQpgYGANCg0KV2hpbGUgbm8uIG9mIHNhbXBsZSBzaXplIGluY3JlYXNlIHRoZSB2YXJpYWJpbGl0eSBvZiBzYW1wbGluZw0KZGlzdHJpYnV0aW9uIGRlY3JlYXNlLGFuZCBUaGUgc2FtcGxlIGRpc3RyaWJ1dGlvbiBtZWFuIHdpbGwgYmUgbm9ybWFsbHkNCmRpc3RyaWJ1dGVkIGFzIGxvbmcgYXMgdGhlIHNhbXBsZSBzaXplIGlzIG1vcmUgdGhhbiAzMC4NCg0KIyBrZXJuZWwgZGlzdHJpYnV0aW9uDQoNCj4gQSBrZXJuZWwgZGlzdHJpYnV0aW9uIGlzIGEgbm9ucGFyYW1ldHJpYyByZXByZXNlbnRhdGlvbiBvZiB0aGUNCj4gcHJvYmFiaWxpdHkgZGVuc2l0eSBmdW5jdGlvbiAoJHBkZiQpIG9mIGEgcmFuZG9tIHZhcmlhYmxlIGluIGFueQ0KPiBwb3B1bGF0aW9uDQoNClRoZSBrZXJuZWwgc21vb3RoaW5nIGZ1bmN0aW9uIGRlZmluZXMgdGhlIHNoYXBlIG9mIHRoZSBjdXJ2ZSB1c2VkIHRvDQpnZW5lcmF0ZSB0aGUgcGRmIEtlcm5lbCBkaXN0cmlidXRpb24gaXMgUXVvdGUgZnJvbSBoaXN0b2dyYW0gaW4gb3RoZXINCndvcmQgKHNtb290aCByZXByZXNlbnRhdGlvbiBvZiBhIGhpc3RvZ3JhbSkgVGhhdCB0aGUgaW50ZWdyYWwgPTEgVGhlcmUNCmlzIGEgYmVuZWZpdCBvZiBzbW9vdGggcmVwcmVzZW50YXRpb24gb2YgYSBoaXN0b2dyYW0gbGlrZSBJZ25vcmVzDQppcnJlZ3VsYXJpdGllcyBhbmQgb3V0bGllcnMgLCBtb3JlIGVmZmljaWVudCBpbiBhcHByb3hpbWF0aW9uIHNvIGl0DQpkZWFscyBiZXR0ZXIgd2l0aCBsYXJnZSBkYXRhIHRoYW4gc21hbGwgZGF0YQ0KDQokJCBcaGF0e2ZfaH0gPSBcZnJhY3sxfXtufSA9IFxzdW1ebl97aSA9IDF9IEsoeC14X2kpICA9IFxmcmFjezF9e25ofSBLXGxlZnQoXGZyYWN7eC14X2l9e2h9XHJpZ2h0KSAkJA0KDQojIyBSdWxlcw0KDQojIyMgTm9uLXdlaWdodGVkIERhdGENCg0KJCQgXGhhdHtmX2h9ID0gXGZyYWN7MX17bn0gXHN1bV5uX3tpID0gMX0gSyh4LXhfaSkgID0gXGZyYWN7MX17bmh9IEtcbGVmdChcZnJhY3t4LXhfaX17aH1ccmlnaHQpICQkDQoNCiMjIyBXZWlnaHRlZCBEYXRhDQoNCiQkDQpcaGF0e2ZfaH0gPSBcZnJhY3sxfXtofSBcc3VtXk5fe2k9MX0gd19pIEsgXGxlZnQoXGZyYWN7eC14X2l9e2h9XHJpZ2h0KSwgXHFxdWFkIFx0ZXh0e3doZXJlfSBcc3VtXk5fe2k9IDF9IHdfaSAgPSAxDQokJA0KDQojIyBLZXJuZWwgRnVuY3Rpb24NCg0KMS4gIEJveA0KMi4gIFRyaWFuZ2xlDQozLiAgTm9ybWFsDQo0LiAgUGFudGVjaG5pY29uDQoNCkVhY2ggZGVuc2l0eSBjdXJ2ZSB1c2VzIHRoZSBzYW1lIGlucHV0IGRhdGEsIGJ1dCBhcHBsaWVzIGEgZGlmZmVyZW50DQprZXJuZWwgc21vb3RoaW5nIGZ1bmN0aW9uIHRvIGdlbmVyYXRlIHRoZSBwZGYuIFRoZSBkZW5zaXR5IGVzdGltYXRlcyBhcmUNCnJvdWdobHkgY29tcGFyYWJsZSwgYnV0IHRoZSBzaGFwZSBvZiBlYWNoIGN1cnZlIHZhcmllcyBzbGlnaHRseS4gRm9yDQpleGFtcGxlLCB0aGUgYm94IGtlcm5lbCBwcm9kdWNlcyBhIGRlbnNpdHkgY3VydmUgdGhhdCBpcyBsZXNzIHNtb290aA0KdGhhbiB0aGUgb3RoZXJzLg0KDQpUaGUgY2hvaWNlIG9mIGJhbmR3aWR0aCB2YWx1ZSBjb250cm9scyB0aGUgc21vb3RobmVzcyBvZiB0aGUgcmVzdWx0aW5nDQpwcm9iYWJpbGl0eSBkZW5zaXR5IGN1cnZlIChoaWdoZXIgdmFsdWUgb2YgJGgkIG1vcmUgc21vb3RoaW5nKQ0KDQpTcGVjaWZ5aW5nIGEgc21hbGxlciBiYW5kd2lkdGggcHJvZHVjZXMgYSB2ZXJ5IHJvdWdoIGN1cnZlLCBidXQgcmV2ZWFscw0KdGhhdCB0aGVyZSBtaWdodCBiZSB0d28gbWFqb3IgcGVha3MgaW4gdGhlIGRhdGEuIFNwZWNpZnlpbmcgYSBsYXJnZXINCmJhbmR3aWR0aCBwcm9kdWNlcyBhIGN1cnZlIG5lYXJseSBpZGVudGljYWwgdG8gdGhlIGtlcm5lbCBmdW5jdGlvbg0KQ2hvb3NpbmcgdGhlIG9wdGltYWwgKCRoJCkgYmFuZHdpZHRoIG1ldGhvZHMgOg0KDQoxLiAgU2lsdmVybWFuJ3MgcnVsZSBvZiB0aHVtcCB0aGF0IGNvbXB1dGVzIGFuIG9wdGltYWwgaCBieSBhc3N1bWluZw0KICAgIHRoYXQgZGF0YSBpcyBub3JtYWxseSBkaXN0cmlidXRlZA0KMi4gIEltcHJvdmVkIFNoZWF0aGVyIEpvbmVzIChJU0opIGFuIGFsZ29yaXRobSBpcyBtb3JlIHJvYnVzdCB3aXRoDQogICAgbXVsdGltb2RhbGl0eSBkYXRhIG9yIGEgbG90IG9mIGRhdGEgKG9uZSBkaXNhZHZhbnRhZ2UgaXMgaXQgbmVlZHMgdG8NCiAgICBsYXJnZSBkYXRhICkNCg0KQm91bmRlZCBkb21haW5zIGRhdGE6IGhhdmUgYSBjb25zdHJhaW5zIGxpa2UgZGF0YSBjb3VsZG4ndCBiZSBuZWdhdGl2ZSAoDQotdmUgbGVhZCB0byBwcm9iYWJpbGl0eSA9IDApDQoNCiMjIE1pcnJvciBtZXRob2QNCg0KMS4gIE1pcnJvciB0aGUgZGF0YQ0KMi4gIFN1bSB0aGUgb3JpZ2luYWwgYW5kIG1pcnJvcmVkIGtlcm5lbCBkZW5zaXR5IGVzdGltYXRlDQozLiAgQ2hvcCBpdCBzbyB0aGF0IHplcm8gYXQgdGhlIGJvdW5kYXJ5IHNpZGUNCg0KIyMgMkQNCg0KJGgkIDogY291bGQgYmUgbWF0cml4IChkaWZmZXJlbnQgJGgkIGluIGRpZmZlcmVudCBkaXJlY3Rpb25zKQ0KDQpUaGUgY2hvaWNlIG9mIG5vcm0gY29tZXMgaW50byAkZCBcZ2UgMiQNCg0KVGhlIHAtbm9ybSBpcyAkfHx4fHxfcCA6PSAoXHN1bV97aT0xfSB8eHxecClee1xmcmFjezF9e3B9fSQgLSBub3JtLXAgPTENCk1hbmhhdHRhbiBkaXN0YW5jZSAtIG5vcm0tcCA9MiBFdWNsaWRlYW4gbm9ybSAtIG5vcm0tcCA9aW5mIG1heGltdW0gbm9ybQ0KKGl0J3Mgbm90IG9idmlvdXMgaW4gZXZlcnkgY2FzZSB3aGljaCBub3JtIGlzIHRoZSBjb3JyZWN0IG9uZSkNCg0Kc3RhbmRhcmQgZXVjbGlkZWFuIGRpc3RhbmNlIGlzIGdvb2QgY2hvaWNlIGJlY2F1c2UgaXQgaW52YXJpYW50IHVuZGVyDQpyb3RhdGlvbiBhcyBsYXJnZSBkYXRhIGNob2ljZSBvZiBrIGFuZCBwIGlzbid0IGltcG9ydGFudCBzbw0KDQojIEFuc3dlcmluZyBvdXIgUXVlc3Rpb25zDQoNCiMjIEFjdXJyYXRlIHN1bW1hcnkgb2Ygb3VyIGRhdGEuDQoNClEzKSBpKQ0KDQppaSk/PyBpaWkpIFllcywgYnkgaW50ZXJ2YWwgZXN0aW1hdGlvbiBpdik/PyBRNCkgUTUpDQoNCmBgYHtyfQ0KZ0FnZURlbnNpdHkgPC0gdHJhaW4gJT4lDQogIHNlbGVjdChBZ2UpICU+JQ0KICBnZ3Bsb3QoYWVzKEFnZSwgeSA9IC4uZGVuc2l0eS4uKSkgKw0KICBnZW9tX2hpc3RvZ3JhbShiaW5zID0gMjAsYmlud2lkdGggPSAxLGNvbG9yPWluZmVybm8oMSxhbHBoYT0xKSkgKyANCiAgZ2VvbV9kZW5zaXR5KGZpbGw9aW5mZXJubygxLGJlZ2luID0gMC41LGFscGhhID0gMC41KSxjb2xvciA9IGluZmVybm8oMSxiZWdpbj0wKSkgKyANCiAgYW5ub3RhdGUoDQogICAgInRleHQiLA0KICAgIHggPSA3MCwNCiAgICB5ID0gMC4wNCwNCiAgICBsYWJlbCA9IHBhc3RlKCJTa2V3bmVzczoiLHNrZXduZXNzKHRyYWluJEFnZSxuYS5ybSA9IFQpKSwNCiAgICBjb2xvdXIgPSBpbmZlcm5vKDEsYmVnaW4gPSAwLjEpLA0KICAgIHNpemUgPSA0DQogICkgKyANCiAgdGhlbWVfaXBzdW1fcmMoKQ0KDQpnQWdlRGVuc2l0eQ0KYGBgDQoNClE2KS8vPw0KDQpgYGB7cn0NCmdGYXJlRGVuc2l0eSA8LSB0cmFpbiAlPiUNCiAgc2VsZWN0KEZhcmUpICU+JQ0KICBnZ3Bsb3QoYWVzKEZhcmUsIHkgPSAuLmRlbnNpdHkuLikpICsNCiAgZ2VvbV9oaXN0b2dyYW0oYmlucyA9IDIwLGJpbndpZHRoID0gMSxjb2xvcj12aXJpZGlzKDEsYWxwaGE9MSkpICsgDQogIGdlb21fZGVuc2l0eShmaWxsPWluZmVybm8oMSxiZWdpbiA9IDAuNSxhbHBoYSA9IDAuNSksY29sb3IgPSB2aXJpZGlzKDEsYmVnaW49MCkpICsgDQogIHNjYWxlX3lfY29udGludW91cyhsaW1pdHMgPSBjKDAsMC4wNSkpKw0KICB0aGVtZV9pcHN1bV9yYygpICsgDQogIGFubm90YXRlKA0KICAgICJ0ZXh0IiwNCiAgICB4ID0gMjAwLA0KICAgIHkgPSAwLjA1LA0KICAgIGxhYmVsID0gcGFzdGUoIlNrZXduZXNzIixza2V3bmVzcyh0cmFpbiRGYXJlKSksDQogICAgY29sb3VyID0gImJsYWNrIiwNCiAgICBzaXplID0gNA0KICApDQoNCmdGYXJlRGVuc2l0eQ0KYGBgDQoNClE4KSBRKTksMTAsMTENCg0KYGBge3J9DQpudW1PZlNhbXBsZXMgPC0gYyg1MCwxMDAsMTAwMCkNCnNtcGxuZ0RzdHJidG9uUnBzQ2huZyA8LSB0aWJibGUoKQ0KZm9yKGkgaW4gbnVtT2ZTYW1wbGVzKXsNCiAgZm9yKHkgaW4gMTppKXsNCiAgICBuc2FtcGxlIDwtIHNhbXBsZV9uKHRyYWluLHNpemU9NTAscmVwbGFjZT1UKSAlPiUNCiAgICAgIHNlbGVjdChBZ2UpDQogICAgbmV3Um93IDwtIG5yb3coc21wbG5nRHN0cmJ0b25ScHNDaG5nKSArIDENCiAgICBzbXBsbmdEc3RyYnRvblJwc0NobmdbbmV3Um93LCJyZXBzIl0gPC0gaQ0KICAgIHNtcGxuZ0RzdHJidG9uUnBzQ2huZ1tuZXdSb3csInhfYmFyIl0gPC0gbWVhbihuc2FtcGxlJEFnZSxuYS5ybSA9IFQpDQogIH0NCn0NCg0KZ1NhbXBsaW5nUmVwcyA8LSBzbXBsbmdEc3RyYnRvblJwc0NobmcgJT4lDQogIHBsb3RfbHkoDQogICAgeCA9IH54X2JhciwNCiAgICBmcmFtZSA9IH5yZXBzLA0KICAgIHR5cGUgPSAiaGlzdG9ncmFtIg0KICApDQoNCmdTYW1wbGluZ1JlcHMNCmBgYA0KDQpRMTIpICBRKTEzLDE0LDE1DQoNCmBgYHtyIGV2YWw9RkFMU0UsIGluY2x1ZGU9RkFMU0V9DQpzaXplcyA8LSBzZXEoMjAsMjAwLDIwKQ0Kc21wbG5nRHN0cmJ0b25TekNobmcgPC0gdGliYmxlKCkNCmZvcihpIGluIHNpemVzKXsNCiAgZm9yKHkgaW4gMToxNTAwKXsNCiAgICBuc2FtcGxlIDwtIHNhbXBsZV9uKHRyYWluLHNpemU9aSxyZXBsYWNlPVQpICU+JQ0KICAgICAgc2VsZWN0KEFnZSkNCiAgICBuZXdSb3cgPC0gbnJvdyhzbXBsbmdEc3RyYnRvblN6Q2huZykgKyAxDQogICAgc21wbG5nRHN0cmJ0b25TekNobmdbbmV3Um93LCJzaXplcyJdIDwtIGkNCiAgICBzbXBsbmdEc3RyYnRvblN6Q2huZ1tuZXdSb3csInhfYmFyIl0gPC0gbWVhbihuc2FtcGxlJEFnZSxuYS5ybSA9IFQpDQogIH0NCn0NCmdTYW1wbGluZ1NpemUgPC0gc21wbG5nRHN0cmJ0b25TekNobmcgJT4lDQogIHBsb3RfbHkoDQogICAgeCA9IH54X2JhciwNCiAgICBmcmFtZSA9IH5zaXplcywNCiAgICB0eXBlID0gImhpc3RvZ3JhbSINCiAgKQ0KDQpnU2FtcGxpbmdTaXplDQpgYGANCg0KUTE2KSBRMTcsMTgpDQoNCmBgYHtyIGV2YWw9RkFMU0UsIGluY2x1ZGU9RkFMU0V9DQpzaXplcyA8LSBzZXEoMiw1MCwyKQ0Kc21wbG5nRHN0cmJ0b25TekNobmcgPC0gdGliYmxlKCkNCmZvcihpIGluIHNpemVzKXsNCiAgZm9yKHkgaW4gMToxNTAwKXsNCiAgICBuc2FtcGxlIDwtIHNhbXBsZV9uKHRyYWluLHNpemU9aSxyZXBsYWNlPVQpICU+JQ0KICAgICAgc2VsZWN0KEFnZSkNCiAgICBuZXdSb3cgPC0gbnJvdyhzbXBsbmdEc3RyYnRvblN6Q2huZykgKyAxDQogICAgc21wbG5nRHN0cmJ0b25TekNobmdbbmV3Um93LCJzaXplcyJdIDwtIGkNCiAgICBzbXBsbmdEc3RyYnRvblN6Q2huZ1tuZXdSb3csInZhcmlhbmNlIl0gPC0gc2QobnNhbXBsZSRBZ2UsbmEucm0gPSBUKSoqMg0KICB9DQp9DQpnU2FtcGxpbmdTaXplIDwtIHNtcGxuZ0RzdHJidG9uU3pDaG5nICU+JQ0KICBwbG90X2x5KA0KICAgIHggPSB+dmFyaWFuY2UsDQogICAgZnJhbWUgPSB+c2l6ZXMsDQogICAgdHlwZSA9ICJoaXN0b2dyYW0iDQogICkNCg0KZ1NhbXBsaW5nU2l6ZQ0KYGBgDQo=